Testing in Python using Pytest and Mock

您所在的位置:网站首页 python testing Testing in Python using Pytest and Mock

Testing in Python using Pytest and Mock

#Testing in Python using Pytest and Mock| 来源: 网络整理| 查看: 265

Why is Unit Testing important?

Testing is important:

To make sure that what we think the code should do is really happeningTo make sure that the application behaviour hasn't changed after other features/bugs were implemented. To ensure that corner cases work and continue to workIt helps avoid bugs. If you don’t test, you'll probably have more bugs. Instead of spending time solving bugs and worrying about them, let’s invest time to avoid them!

Ok. So now you know why testing is so crucial. But what is Unit Testing?

Unit Testing is testing each software component separately like functions and methods.

Unit Testing is key to:

Facilitate the process to locate the problem as we would know which code block the problem is in. If we test the software as a whole we would need to search for the block where the problem is.Pytest 

Running pytest without mentioning a filename will run all files of the format test_*.py or *_test.py in the current directory and subdirectories.

The advantage of naming test filenames as %_test.py is that the file that has the test of a module will be close to the file if we list the files alphabetically. If we name the test files as test_%.py, it will list all test files together. Some people prefer to have the test files in a folder structure separated from the codebase, usually in a directory called tests.

Inside the Pytest test files, create functions which names starts with “test_”. Each of those functions will be counted as one item on the report that shows when you run pytest.

Fixtures

Fixtures are Pytest functions that contain data or get data from other files like configuration files. It also can be used to load test data into the database and set environment variables to be used during the tests.

Here is an example of a module pytesting.py that we will use in an example:

No alt text provided for this image

Here we start the test file importing what we will use:

No alt text provided for this image

Now, we create a fixture that has data of one of the products we will use as an example:

No alt text provided for this image

Here we have a fixture with an example of another product:

No alt text provided for this image

Then, we create another fixture that uses the other two fixtures:

No alt text provided for this image

Finally, we use the fixture in a test:

No alt text provided for this image

It is possible to have the fixtures in the same file as the tests or in a conftest.py file. Having them in a conftest.py file, we can use them in all test files of the project. We also can have a conftest.py file in each folder. This way we can have a hierarchy and each folder has its own conftest.py file and fixtures.

Fixture Scopes

Pytest Fixture scopes specify how often a fixture will be executed:

Function: The default scope. To specify explicitly use scope="function". This is executed every test function it is called. So, if a fixture is used in two functions, it will be called twice.Class:  scope="class". Every time a test class is called, this is executed. So, if a fixture is used in two classes, it will be called twice, no matter how many methods are inside those classes.Module:  scope="module". This is executed every test module it is called. So, if a fixture is used in two modules, it will be called twice, no matter how many methods are inside those modules.Package:  scope="package". It is executed every time a test package is called. So, if a fixture is used in two packages, it will be called twice, no matter how many methods are inside those packages.Session:  scope="session". This is executed every session. A session is a time when pytest runs. An use case would be setting up the test database.Markers

Pytest Markers are used to create categories to select which categories we want to run or exclude when running Pytest.

Running pytest --markers we see all the markers that Pytest is aware of.

To define a marker in a 

pytest -v -m "not marker_name"

Parametrization

With parametrization, we can do several tests using less code.

We will create a test using the same add function that as before:

No alt text provided for this image

Here we do one test with 3 test cases without much additional code:

No alt text provided for this imageAnalyzing the duration of tests

Executing Pytest with the parameter --durations=X, we can see the duration of the X slowest tests.

Mocking

The English meaning of mocking is to make a replica or imitation of something.

In unit testing, a code block that you want to test may depend on other code. When we unit test, we test only a code block without those dependencies. To isolate the behavior of the code you need to test, you need to replace the other code with mocks that simulate the behavior of the real code. So, mocking is using mocks that simulate real code behavior.

A Simple Mocking Example

Here is a simple example. We mock an object with a method called “method_example”. We tested:

The .call_count shows the number of times method_example was called and we are testing if after the first call .call_count returns 1. With .assert_called() we tested if method_example was called. With .assert_called_once() we tested if method_example was called only once.With .assert_called_with("parameter example") we tested if method_example was called with the parameter "parameter example".With .assert_called_once_with("parameter example") we tested if method_example was called only once with the parameter "parameter example".Afterwards, we tested if .call_count returned 2 after the second call to method_example.With .assert_called_with() we tested if method_example was called without any parameters.No alt text provided for this imageExample of a function in another file

Here is a simple function run_dice that calls random.randint to simulate a dice roll. And the function returns "Unlucky" if we get a number less than 4 and "Lucky" otherwise. This code is in the file mocking.py.

No alt text provided for this image

This is the test. With @patch("testing.mocking.random"), we mock random from the mocking.py file which is the file where run_dice is located and is in the testing directory. The mock_random parameter of the test_mock_function_with_dice method is the name of the mock object we pass to the test method. Then inside the function, we say that randint should return 4. Then we execute the run_dice function and test if it results in "Lucky". Finally, we say that randint should return 3 then we execute the run_dice function and test if it returns "Unlucky".

No alt text provided for this imageMocking using pytest-mock

Now we will mock using pytest-mock. Therefore, for this to work we need to install it:

$ pip install pytest-mock

Here is how we would do the same test shown before using pytest-mock. The fixture mocker was created by pytest-mock.

No alt text provided for this imageConclusion

In this article I explained how to use Pytest and Mock to implement tests in Python. I discussed basic use, fixtures, markers, parametrization and duration. With this you have a comprehensive toolset to test your Python application.

This is part of a series about Python recommended coding practices. The code for this series is also available in this GitHub project.

This article has an index to my other articles.

What are your thoughts on the article? Are there any suggestions, criticisms, or opinions? Let me know in the comments!

#python #pythontests #pytest  #pythonbestpractices #bestpractices #pythonprogramming #codingpractices #codingskills #codingtips



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3